#include "protocol_api.h"
#include "protocol_app.h"
#include "protocol_list.h"
#include "protocol_board.h"

/**
 * @brief 初始化消息队列
 *
 */
static void ProtocolInitialisingMessagequeue(void);

/**
 * @brief 删除一个消息报文
 *
 * @param Node - 对应消息列表
 */
static void ProtocolDeleteMessagePacket(MessageListNode_t Node);

/**
 * @brief 获取待发送的文件
 *
 * @return MessageListNode_t - 存在着返回对应缓存文件表，不存在则返回NULL
 */
static MessageListNode_t ProtocolGetMessagePacket(void);

/**
 * @brief 删除所有消息报文
 *
 */
static void ProtocolDeleteAllMessagePacket(void);

/**
 * @brief 向目标设备发送一定长度的数据
 *
 * @param Target  - 目标设备
 * @param pDataIn - 待发送的数据
 * @param SizeIn  - 待发送数据长度
 */
static void ProtocolSendDataToTaegetDevice(DataSource_t Target, const uint8_t *pDataIn, uint16_t SizeIn);

/**
 * @brief 缓存要发送的消息
 *
 * @param Target   - 目标设备
 * @param pDataIn  - 待传输的数据
 * @param SizeIn   - 传输数据长度
 * @param Command  - 命令
 * @param SearchId - 检索编码
 * @param AckEn    - 是否需要等待应答
 * @return bool    - 缓存状态（true：成功 false：失败）
 */
static bool ProtocolCacheMessageToBeSend(uint8_t Target, const void *pDataIn, uint16_t SizeIn, uint8_t Command, uint32_t SearchId, bool AckEn);

/**
 * @brief 报文发送回调任务
 *
 */
static void ProtocolMessageSendCallback(void);

/**
 * @brief 报文解析回调任务
 *
 * @param pDataPacketIn - 待解析的报文
 */
static void MessageParsingCallback(const void *pDataPacketIn);

static MessagingTaskStatusBit_t MessagingTaskStatusBit = {0};
static MessageListNode_t MessageHeadNode = NULL, FileListNode = NULL;

static void ProtocolInitialisingMessagequeue(void)
{
  MessageHeadNode = MessageCreateTableList();
}

static MessageListNode_t ProtocolGetMessagePacket(void)
{
  MessageListNode_t MixListNode = NULL;
  MessageListNode_t TmpListNode = MessageHeadNode->NextItem;

  if (TmpListNode != MessageHeadNode)
  {
    MixListNode = TmpListNode;

    while (TmpListNode != MessageHeadNode)
    {
      if (MixListNode->Message.TriggerTime > TmpListNode->Message.TriggerTime)
      {
        MixListNode = TmpListNode;
      }

      TmpListNode = TmpListNode->NextItem;
    }
  }

  return MixListNode;
}

static void ProtocolDeleteMessagePacket(MessageListNode_t Node)
{
  MessageListErase(Node);
}

static void ProtocolDeleteAllMessagePacket(void)
{
  while (1)
  {
    MessageListNode_t FileListNode = ProtocolGetMessagePacket();
    if (FileListNode == NULL)
      break;

    ProtocolDeleteMessagePacket(FileListNode);
  }
}

static void ProtocolSendDataToTaegetDevice(DataSource_t Target, const uint8_t *pDataIn, uint16_t SizeIn)
{
  if (pDataIn != NULL)
  {
    if (SizeIn > 0)
    {
      switch (Target)
      {
      case SOURCE_MOBILE_CLIENT:
        ProtocolSendDataToBluetoothHost(pDataIn, SizeIn);
        break;

      case SOURCE_NETWORK_TRANSMISSION:
        ProtocolSendDataToNetwok(pDataIn, SizeIn);
        break;

      default:
        break;
      }
    }
  }
}

static bool ProtocolCacheMessageToBeSend(uint8_t Target, const void *pDataIn, uint16_t SizeIn, uint8_t Command, uint32_t SearchId, bool AckEn)
{
  if (pDataIn != NULL)
  {
    if ((SizeIn > 0) && (SizeIn < MESSAGE_DATA_LENGTH))
    {
      MessageDataPacket_t MessageDataPacket = {0};

      MessageDataPacket.Response = AckEn;
      MessageDataPacket.Command = Command;
      MessageDataPacket.DataSource = Target;
      MessageDataPacket.PacketSize = SizeIn;
      MessageDataPacket.BasisOfSearch = SearchId;
      memcpy(MessageDataPacket.PacketData, pDataIn, SizeIn);
      MessageDataPacket.TriggerTime = ProtocolMessageGetSystemClockTime();

      if (MessageListPushBack(MessageHeadNode, &MessageDataPacket) == true)
      {
        if (ProtocolGetMessageQueueBusyEn() != true)
        {
          memset(&MessagingTaskStatusBit, 0, sizeof(MessagingTaskStatusBit_t));
          ProtocolStartSendingTaskNoWait();

          return true;
        }
      }
      else
      {
        PRINT("[message]: not enough space to store.\r\n");
      }
    }
  }
  return false;
}

static void ProtocolMessageSendCallback(void)
{
  switch (MessagingTaskStatusBit.ResponseStatus)
  {
  case MESSAGE_EXECUTE_IDLE:
  {
    FileListNode = ProtocolGetMessagePacket();
    if (FileListNode != NULL)
    {
      bool StartNetworkAttachment = false;

      if (ProtocolGetNetworkAttachmentStatus() != NETWORK_ATTACHMENT_SUCCESS)
      {
        if (ProtocolStartNetworkAttachmentTask() == true)
        {
          PRINT("[message]: network not ready, start network attachment.\r\n");

          StartNetworkAttachment = true;
          MessagingTaskStatusBit.ResponseStatus = MESSAGE_WAIT_FOR_NETWORK_READY;
        }
      }
      else
      {
        PRINT("[message]: network is ready. go to transmit.\r\n");

        StartNetworkAttachment = true;
        MessagingTaskStatusBit.ResponseStatus = MESSAGE_RESPONSES_TRANSMITTER;
      }

      if (StartNetworkAttachment = true)
      {
        MessagingTaskStatusBit.SendStatus = MESSAGE_SEND_IDLE;
        ProtocolSetMessageQueueBusyEn(true);
      }
      else
      {
        PRINT("[message]: network attachment failure, start task error.\r\n");

        ProtocolDeleteAllMessagePacket();
        MessagingTaskStatusBit.SendStatus = MESSAGE_SEND_NETWORK_ATT_FAILURE;
      }

      ProtocolStartSendingTaskNoWait();
    }
    else
    {
      PRINT("[message]: no message to be sent.\r\n");

      switch (MessagingTaskStatusBit.SendStatus)
      {
      case MESSAGE_SEND_HOST_NO_ACK:
        ProtocolRemindersConnectionAnomaly();
        break;

      case MESSAGE_SEND_COMPLETION:
        ProtocolPromptToNetworkAttachmentEnd();
        break;

      default:
        ProtocolPromptToNetworkAttachmentFailure();
        break;
      }

      ProtocolSetMessageQueueBusyEn(false);
    }
  }
  break;

  case MESSAGE_WAIT_FOR_NETWORK_READY:
  {
    ProtocolNetAttaResult_t Result = ProtocolGetNetworkAttachmentStatus();
    if ((Result == NETWORK_ATTACHMENT_SUCCESS) || (Result == NETWORK_ATTACHMENT_ERROR))
    {
      if (Result == NETWORK_ATTACHMENT_ERROR)
      {
        PRINT("[message]: network attachment failure.\r\n");

        MessagingTaskStatusBit.ResponseStatus = MESSAGE_EXECUTE_IDLE;
        MessagingTaskStatusBit.SendStatus = MESSAGE_SEND_NETWORK_ATT_FAILURE;
        ProtocolDeleteAllMessagePacket();
      }
      else
      {
        PRINT("[message]: network is ready. go to transmit.\r\n");
        MessagingTaskStatusBit.ResponseStatus = MESSAGE_RESPONSES_TRANSMITTER;
      }
    }

    ProtocolDelayedStartSendingTask(PROTOCOL_INTERVAL_QUERY_NETWORK_STATUS_TIME);
  }
  break;

  case MESSAGE_RESPONSES_TRANSMITTER:
  {
    PRINT("[message]: send data to host: length: %d, data: ", FileListNode->Message.PacketSize);
    for (uint16_t i = 0; i < FileListNode->Message.PacketSize; i++)
      PRINT("%02X", FileListNode->Message.PacketData[i]);
    PRINT("\r\n");

    ProtocolSendDataToTaegetDevice(FileListNode->Message.DataSource, FileListNode->Message.PacketData, FileListNode->Message.PacketSize);
    if (FileListNode->Message.Response == true)
    {
      PRINT("[message]: waiting for host to answer.\r\n");

      MessagingTaskStatusBit.ResponseStatus = MESSAGE_RESPONSES_TIMEOUT;
      ProtocolDelayedStartSendingTask(MESSAGE_RESTART_SENDING_INTERVAL);
    }
    else
    {
      PRINT("[message]: sent successfully.\r\n");

      MessagingTaskStatusBit.ResponseStatus = MESSAGE_COMPLETION_EXECUTION;
      ProtocolStartSendingTaskNoWait();
    }
  }
  break;

  case MESSAGE_RESPONSES_TIMEOUT:
  {
    MessagingTaskStatusBit.NumberOfSend++;
    if (MessagingTaskStatusBit.NumberOfSend > MESSAGE_ALLPWED_NUMBER_OF_SEND)
    {
      PRINT("[message]: failed to send.\r\n");

      MessagingTaskStatusBit.SendStatus = MESSAGE_SEND_HOST_NO_ACK;
      MessagingTaskStatusBit.ResponseStatus = MESSAGE_COMPLETION_FAILURE;
    }
    else
    {
      PRINT("[message:] wait for host to answer timeout.\r\n");
      MessagingTaskStatusBit.ResponseStatus = MESSAGE_RESPONSES_TRANSMITTER;
    }

    ProtocolStartSendingTaskNoWait();
  }
  break;

  case MESSAGE_COMPLETION_FAILURE:
  case MESSAGE_COMPLETION_EXECUTION:
  {
    if (MessagingTaskStatusBit.ResponseStatus == MESSAGE_COMPLETION_FAILURE)
    {
      ProtocolDeleteAllMessagePacket();
    }
    else
    {
      ProtocolDeleteMessagePacket(FileListNode);
    }

    ProtocolStartSendingTaskNoWait();
    MessagingTaskStatusBit.ResponseStatus = MESSAGE_EXECUTE_IDLE;
  }
  break;
  }
}

static void MessageParsingCallback(const void *pDataPacketIn)
{
  if (pDataPacketIn != NULL)
  {
    uint32_t SearchId = 0;
    uint8_t AnswerCommand = 0;
    uint16_t AnswerDataLength = 0;
    uint8_t AnswerData[NOMINAL_PACKET_LENGTH] = {0};
    ProtocolRxMessageFormat_t *pRxMessageFormat = (ProtocolRxMessageFormat_t *)pDataPacketIn;

    ProtocolParsingInputMessages(pRxMessageFormat->AttachedData, pRxMessageFormat->SizeOfData, AnswerData, &AnswerDataLength, &AnswerCommand, &SearchId);
    if (FileListNode != NULL)
    {
      if (SearchId == FileListNode->Message.BasisOfSearch)
      {
        PRINT("[message]: successful host reception.\r\n");

        MessagingTaskStatusBit.NumberOfSend = 0;
        MessagingTaskStatusBit.SendStatus = MESSAGE_SEND_COMPLETION;
        MessagingTaskStatusBit.ResponseStatus = MESSAGE_COMPLETION_EXECUTION;
        ProtocolStartSendingTaskNoWait();

        return;
      }
    }

    if (pRxMessageFormat->DataSource != SOURCE_MOBILE_CLIENT)
    {
      PRINT("999999\r\n");
      ProtocolResetNetworkKeepLiveTimer();
      ProtocolCacheMessageToBeSend(pRxMessageFormat->DataSource, AnswerData, AnswerDataLength, AnswerCommand, SearchId, false);
    }
    else
    {
      PRINT("888888\r\n");
      ProtocolSendDataToTaegetDevice(SOURCE_MOBILE_CLIENT, AnswerData, AnswerDataLength);
    }
  }
}

bool ProtocolSendHeartbeatToHost(void)
{
#if _NETWOK_FUNCTION_ENABLE_
  uint16_t MessageDataLength = 0;
  uint32_t SearchId = (uint32_t)rand();
  uint8_t MessageDataPacket[PROTOCOL_LENGTH_OF_SINGLE_MESSAGE_PACKET] = {0};

  uint8_t DeviceType = ProtocolGetDeviceType();
  uint8_t Rssi = ProtocolGetWirelessCommunicationQuality();
  uint8_t PercentageOfPower = ProtocolGetVoltagePercentage();
  if (ProtocolBuildHeartbeatMessage(DeviceType, PercentageOfPower, Rssi, SearchId, MessageDataPacket, &MessageDataLength) == true)
    return ProtocolCacheMessageToBeSend(SOURCE_NETWORK_TRANSMISSION, MessageDataPacket, MessageDataLength, SYSTEM_HEARTBEAT_CMD, SearchId, true);
#endif

  return false;
}

bool ProtocolActivePushHeartbeatToHost(void)
{
#if _NETWOK_FUNCTION_ENABLE_
  uint16_t MessageDataLength = 0;
  uint32_t SearchId = (uint32_t)rand();
  uint8_t MessageDataPacket[PROTOCOL_LENGTH_OF_SINGLE_MESSAGE_PACKET] = {0};

  uint8_t DeviceType = ProtocolGetDeviceType();
  uint8_t Rssi = ProtocolGetWirelessCommunicationQuality();
  uint8_t PercentageOfPower = ProtocolGetVoltagePercentage();
  if (ProtocolBuildActivePushMessage(DeviceType, PercentageOfPower, Rssi, SearchId, MessageDataPacket, &MessageDataLength) == true)
    return ProtocolCacheMessageToBeSend(SOURCE_NETWORK_TRANSMISSION, MessageDataPacket, MessageDataLength, SYSTEM_HEARTBEAT_CMD, SearchId, true);
#endif

  return false;
}

bool ProtocolUploadOnlineRegisterResult(ReportCardholderInformationFormat_t *pInformationIn, uint8_t Result)
{
#if _NETWOK_FUNCTION_ENABLE_
  if (pInformationIn != NULL)
  {
    uint16_t MessageDataLength = 0;
    uint32_t SearchId = (uint32_t)rand();
    uint8_t MessageDataPacket[PROTOCOL_LENGTH_OF_SINGLE_MESSAGE_PACKET] = {0};

    if (ProtocolBuildOnlineRegisterResultMessage(pInformationIn, Result, SearchId, MessageDataPacket, &MessageDataLength) == true)
      return ProtocolCacheMessageToBeSend(SOURCE_NETWORK_TRANSMISSION, MessageDataPacket, MessageDataLength, CARDHOLDER_REGISTERED_INFORMATION, SearchId, true);
  }
#endif

  return false;
}

bool ProtocolUploadLogEventToHost(uint16_t LogId, uint8_t EventType, uint8_t Parameter, const uint8_t *pValueIn)
{
#if _NETWOK_FUNCTION_ENABLE_
  uint16_t MessageDataLength = 0;
  uint32_t SearchId = (uint32_t)rand();
  RealUploadEventFormat_t RealUploadEventFormat = {0};
  uint8_t MessageDataPacket[PROTOCOL_LENGTH_OF_SINGLE_MESSAGE_PACKET] = {0};

  RealUploadEventFormat.Type = EventType;
  RealUploadEventFormat.Parameter = Parameter;
  RealUploadEventFormat.TriggerTime = ProtocolGetCurrentTimestamp();
  if (pValueIn != NULL)
  {
    memcpy(RealUploadEventFormat.DataPacket, pValueIn, LOG_ATTACHED_DATA_LENGTH);
  }

  if (ProtocolBuildIncidentReportingMessage(LogId, &RealUploadEventFormat, SearchId, MessageDataPacket, &MessageDataLength) == true)
    return ProtocolCacheMessageToBeSend(SOURCE_NETWORK_TRANSMISSION, MessageDataPacket, MessageDataLength, LOG_REAL_TIME_EVENT_REPORTING, SearchId, true);
#endif

  return false;
}

bool ProtocolUploadKeyValueToHost(uint8_t KeyType, const void *pKeyValueIn, uint8_t NumberOfKey)
{
#if _NETWOK_FUNCTION_ENABLE_
  if (pKeyValueIn != NULL)
  {
    if ((NumberOfKey > 0) && (NumberOfKey <= TYPE_THE_LENGTH_OF_KEY))
    {
      uint16_t MessageDataLength = 0;
      uint32_t SearchId = (uint32_t)rand();
      ExterlalValidationeFormat_t ExterlalValidationeFormat = {0};
      uint8_t MessageDataPacket[PROTOCOL_LENGTH_OF_SINGLE_MESSAGE_PACKET] = {0};

      ExterlalValidationeFormat.KeyType = KeyType;
      memcpy(ExterlalValidationeFormat.KeyValue, pKeyValueIn, NumberOfKey);

      if (ProtocolBuildExternalHostValidationMessage(&ExterlalValidationeFormat, SearchId, MessageDataPacket, &MessageDataLength) == true)
        return ProtocolCacheMessageToBeSend(SOURCE_NETWORK_TRANSMISSION, MessageDataPacket, MessageDataLength, OTHER_EXTERL_HOST_VALIDATION, SearchId, true);
    }
  }
#endif

  return false;
}

void ProtocolPraseMessage(uint8_t DataSource, const uint8_t *pMessageIn, const uint16_t SizeIn)
{
  ProtocolThrowingEvent(DataSource, pMessageIn, SizeIn);
}

void ProtocolResetMessageQueue(void)
{
  memset(&MessagingTaskStatusBit, 0, sizeof(MessagingTaskStatusBit_t));
  ProtocolDeleteAllMessagePacket();
}

void ProtocolConfiguration(void)
{
  ProtocolCenterExternalInterfaces_t ExternalInterfaces = {0};

  ExternalInterfaces.ProtocolParsingTask = MessageParsingCallback;
  ExternalInterfaces.ProtocolSendingTask = ProtocolMessageSendCallback;

  ProtocolInitialisingMessagequeue();
  ProtocolCenterInitialisation(&ExternalInterfaces);

  memset(&MessagingTaskStatusBit, 0, sizeof(MessagingTaskStatusBit_t));
}
